#!/bin/sh
# Format python source codes with autopep8 on changed lines.
#
# Usage:
#   format all uncommitted lines:
#   > p8ln
#
#   format file:
#   > p8ln a.py b.py
#
# ---
# # Explaination:
#
# # show all changed file names:
#   git diff --name-only --relative HEAD
#
# And a helper script `git-changed-ln` outputs changed line numbers,
# by diff work-tree(unstaged) with HEAD,
# or by diff index(staged or cached) with HEAD.
#include 'shlib.sh' begin
#!/bin/sh
shlib_init_colors()
{
    Black="$(                   tput setaf 0)"
    BlackBG="$(                 tput setab 0)"
    DarkGrey="$(     tput bold; tput setaf 0)"
    LightGrey="$(               tput setaf 7)"
    LightGreyBG="$(             tput setab 7)"
    White="$(        tput bold; tput setaf 7)"
    Red="$(                     tput setaf 1)"
    RedBG="$(                   tput setab 1)"
    LightRed="$(     tput bold; tput setaf 1)"
    Green="$(                   tput setaf 2)"
    GreenBG="$(                 tput setab 2)"
    LightGreen="$(   tput bold; tput setaf 2)"
    Brown="$(                   tput setaf 3)"
    BrownBG="$(                 tput setab 3)"
    Yellow="$(       tput bold; tput setaf 3)"
    Blue="$(                    tput setaf 4)"
    BlueBG="$(                  tput setab 4)"
    LightBlue="$(    tput bold; tput setaf 4)"
    Purple="$(                  tput setaf 5)"
    PurpleBG="$(                tput setab 5)"
    Pink="$(         tput bold; tput setaf 5)"
    Cyan="$(                    tput setaf 6)"
    CyanBG="$(                  tput setab 6)"
    LightCyan="$(    tput bold; tput setaf 6)"
    NC="$(                      tput sgr0)" # No Color
}
screen_width()
{
    local chr="${1--}"
    chr="${chr:0:1}"
    local width=$(tput cols 2||echo 80)
    width="${COLUMNS:-$width}"
    echo $width
}
hr()
{
    # generate a full screen width horizontal ruler
    local width=$(screen_width)
    printf -vl "%${width}s\n" && echo ${l// /$chr};
}
remove_color()
{
    # remove color control chars from stdin or first argument
    local sed=gsed
    which -s $sed || sed=sed
    local s="$1"
    if [ -z "$s" ]; then
        $sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g"
    else
        echo "$s" | remove_color
    fi
}
text_hr()
{
    # generate a full screen width sperator line with text.
    # text_hr "-" "a title"
    # > a title -----------------------------------------
    #
    # variable LR=l|m|r controls alignment
    local chr="$1"
    shift
    local bb="$(echo "$@" | remove_color)"
    local text_len=${#bb}
    local width=$(screen_width)
    let width=width-text_len
    local lr=${LR-m}
    case $lr in
        m)
            let left=width/2
            let right=width-left
            echo "$(printf -vl "%${left}s\n" && echo ${l// /$chr})$@$(printf -vl "%${right}s\n" && echo ${l// /$chr})"
            ;;
        r)
            echo "$(printf -vl "%${width}s\n" && echo ${l// /$chr})$@"
            ;;
        *)
            # l by default
            echo "$@$(printf -vl "%${width}s\n" && echo ${l// /$chr})"
            ;;
    esac
}
SHLIB_LOG_VERBOSE=1
SHLIB_LOG_FORMAT='[$(date +"%Y-%m-%d %H:%M:%S")] $level $title $mes'
die()
{
    err "$@" >&2
    exit 1
}
die_empty()
{
    if test -z "$1"
    then
        shift
        die empty: "$@"
    fi
}
set_verbose()
{
    SHLIB_LOG_VERBOSE=${1-1}
}
log()
{
    local color="$1"
    local title="$2"
    local level="$_LOG_LEVEL"
    shift
    shift
    local mes="$@"
    local NC="$(tput sgr0)"
    if [ -t 1 ]; then
        title="${color}${title}${NC}"
        level="${color}${level}${NC}"
    fi
    eval "echo \"$SHLIB_LOG_FORMAT\""
}
dd()
{
    debug "$@"
}
debug()
{
    if [ ".$SHLIB_LOG_VERBOSE" = ".1" ]; then
        local LightCyan="$(tput bold ; tput setaf 6)"
        _LOG_LEVEL=DEBUG log "$LightCyan" "$@"
    fi
}
info()
{
    local Brown="$(tput setaf 3)"
    _LOG_LEVEL=" INFO" log "$Brown" "$@"
}
ok() {
    local Green="$(tput setaf 2)"
    _LOG_LEVEL="   OK" log "${Green}" "$@"
}
err() {
    local Red="$(tput setaf 1)"
    _LOG_LEVEL="ERROR" log "${Red}" "$@"
}
git_hash()
{
    git rev-parse $1 \
        || die "'git_hash $@'"
}
git_is_merge()
{
    test $(git cat-file -p "$1" | grep "^parent " | wc -l) -gt 1
}
git_parents()
{
    git rev-list --parents -n 1 ${1-HEAD} | { read self parents; echo $parents; }
}
git_rev_list()
{
    # --parents
    # print parent in this form:
    #     <commit> <parent-1> <parent-2> ..
    git rev-list \
        --reverse \
        --topo-order \
        --default HEAD \
        --simplify-merges \
        "$@" \
        || die "'git rev-list $@'"
}
git_tree_hash()
{
    git rev-parse "$1^{tree}"
}
git_ver()
{
    local git_version=$(git --version | awk '{print $NF}')
    local git_version_1=${git_version%%.*}
    local git_version_2=${git_version#*.}
    git_version_2=${git_version_2%%.*}
    printf "%03d%03d" $git_version_1 $git_version_2
}
git_working_root()
{
    git rev-parse --show-toplevel
}
git_branch_default_remote()
{
    local branchname=$1
    git config --get branch.${branchname}.remote
}
git_branch_default_upstream_ref()
{
    local branchname=$1
    git config --get branch.${branchname}.merge
}
git_branch_default_upstream()
{
    git_branch_default_upstream_ref "$@" | sed 's/^refs\/heads\///'
}
git_head_branch()
{
    git symbolic-ref --short HEAD
}
git_commit_copy()
{
    # We're going to set some environment vars here, so
    # do it in a subshell to get rid of them safely later
    dd copy_commit "{$1}" "{$2}" "{$3}"
    git log -1 --pretty=format:'%an%n%ae%n%ad%n%cn%n%ce%n%cd%n%s%n%n%b' "$1" |
    (
    read GIT_AUTHOR_NAME
    read GIT_AUTHOR_EMAIL
    read GIT_AUTHOR_DATE
    read GIT_COMMITTER_NAME
    read GIT_COMMITTER_EMAIL
    read GIT_COMMITTER_DATE
    export  GIT_AUTHOR_NAME \
        GIT_AUTHOR_EMAIL \
        GIT_AUTHOR_DATE \
        GIT_COMMITTER_NAME \
        GIT_COMMITTER_EMAIL \
        GIT_COMMITTER_DATE
    # (echo -n "$annotate"; cat ) |
    git commit-tree "$2" $3  # reads the rest of stdin
    ) || die "Can't copy commit $1"
}
git_objetct_type()
{
    # $0 ref|hash
    # output "commit", "tree" etc
    git cat-file -t "$@" 2>/dev/null
}
git_copy_commit()
{
    git_commit_copy "$@"
}
git_diff_ln_new()
{
    # output changed line number of a file: <from> <end>; inclusive:
    # 27 28
    # 307 309
    # 350 350
    #
    # Usage:
    #
    #   diff working tree with HEAD:
    #       git_diff_ln_new HEAD -- <fn>
    #
    #   diff working tree with staged:
    #       git_diff_ln_new -- <fn>
    #
    #   diff staged(cached) with HEAD:
    #       git_diff_ln_new --cached -- <fn>
    #
    # in git-diff output:
    # for add lines:
    # @@ -53 +72,8
    #
    # for remove lines:
    # @@ -155 +179,0
    git diff -U0 "$@" \
        | grep '^@@' \
        | awk '{
    # @@ -155 +179,0
    # $1 $2   $3
    l = $3
    gsub("^+", "", l)
    # add default offset: ",1"
    split(l",1", x, ",")
    # inclusive line range:
    x[2] = x[1] + x[2] - 1
    # line remove format: @@ -155, +179,0
    # do need to output line range for removed.
    if (x[2] >= x[1]) {
        print x[1] " " x[2]
    }
}'
}
os_detect()
{
    local os
    case $(uname -s) in
        Linux)
            os=linux ;;
        *[bB][sS][dD])
            os=bsd ;;
        Darwin)
            os=mac ;;
        *)
            os=unix ;;
    esac
    echo $os
}
mac_ac_power_connection()
{
    #  Connected: (Yes|No)
    system_profiler SPPowerDataType \
        | sed '1,/^ *AC Charger Information:/d' \
        | grep Connected:
}
mac_power()
{
    # $0 is-battery          exit code 0 if using battery.
    # $0 is-ac-power         exit code 0 if using ac power.
    local cmd="$1"
    local os=$(os_detect)
    if [ "$os" != "mac" ]; then
        err "not mac but: $os"
        return 1
    fi
    case $cmd in
        is-battery)
            mac_ac_power_connection | grep -q No
            ;;
        is-ac-power)
            mac_ac_power_connection | grep -q Yes
            ;;
        *)
            err "invalid cmd: $cmd"
            return 1
            ;;
    esac
}
fn_match()
{
    # $0 a.txt *.txt
    case "$1" in
        $2)
            return 0
            ;;
    esac
    return 1
}
#include 'shlib.sh' end
cmd_reverse=tac
which -s $cmd_reverse || cmd_reverse="tail -r"
{
    if [ "$#" -gt 0 ]; then
        # fns specified by command line args
        while [ "$#" -gt 0 ]; do
            echo "$1"
            shift
        done
    else
        # find fns by diff
        # get changed fn
        git diff --name-only --relative HEAD
    fi
} | while read fn; do
    # reverse order, autopep8 may add or remove lines
    echo formating "$fn" ...
    git_diff_ln_new HEAD -- "$fn"
    git_diff_ln_new HEAD -- "$fn" \
        | $cmd_reverse \
        | while read f t; do
            autopep8 -i --line-range $f $t "$fn"
        done
done
